package com.simons.common.ftp;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import com.simons.common.exception.CommonErrorType;
import com.simons.common.exception.CommonException;

import lombok.extern.log4j.Log4j2;

@Log4j2
public abstract class BaseFTPOper {
	public FTPClient ftpClient;

	public static final String FTP_SRC_ENCODING = "UTF-8";
	public static final String FTP_DEST_ENCODING = "UTF-8";

	public void download(String remotefileName, String localFile) throws IOException {
		FileOutputStream os = null;
		ftpClient.enterLocalPassiveMode();
		try {
			File file = new File(localFile);
			log.info("fileName:" + remotefileName + ";localFile:" + localFile);

			File parent = file.getParentFile();

			if (!parent.exists()) {
				parent.mkdirs();
			}
			if (!file.exists()) {
				file.createNewFile();
			}

			os = new FileOutputStream(file);
			this.ftpClient.retrieveFile(remotefileName, os);
		} catch (Exception e) {
			log.error("", e);
		} finally {
			IOUtils.closeQuietly(os);
		}
	}

	public void downloadFile(String remotefileName, String localFileName, String validate, boolean recursion)
			throws IOException {
		FileOutputStream os = null;

		try {
			this.ftpClient.changeWorkingDirectory(remotefileName);
			FTPFile[] files = this.ftpClient.listFiles();

			for (FTPFile file : files) {
				if (file.isDirectory() && recursion) {
					downloadFile(remotefileName + file.getName() + "/", localFileName + file.getName() + "/", validate,
							recursion);

					continue;
				}
				String fileName = new String(localFileName.getBytes(FTP_SRC_ENCODING), FTP_DEST_ENCODING) + "/"
						+ new String(file.getName().getBytes(FTP_SRC_ENCODING), FTP_DEST_ENCODING);
				File localFile = new File(fileName);

				if (!localFile.getParentFile().exists()) {
					localFile.getParentFile().mkdirs();
				}

				if (file.getName().endsWith(validate)) {
					this.downloadFile(ftpClient, file, localFile);
				}
			}
		} finally {
			IOUtils.closeQuietly(os);
		}
	}

	private void downloadFile(FTPClient ftpClient, FTPFile file, File localFile) {
		OutputStream is = null;
		// 输出
		try {
			is = new FileOutputStream(localFile);
			// 下载文件
			ftpClient.retrieveFile(file.getName(), is);
		} catch (Exception e) {
			log.error("", e);
		} finally {
			IOUtils.closeQuietly(is);
		}
	}

	public long downloadFileOnTime(String remotefileName, String localFileName, String validate, boolean recursion,
			long timeStamp) throws IOException {
		FileOutputStream os = null;
		Long newestTime = Long.valueOf("0");

		try {
			this.ftpClient.changeWorkingDirectory(remotefileName);
			ftpClient.enterLocalPassiveMode();
			FTPFile[] files = this.ftpClient.listFiles();

			for (FTPFile file : files) {
				if (file.isDirectory() && recursion) {
					Long time = downloadFileOnTime(remotefileName + file.getName() + "/",
							localFileName + file.getName() + "/", validate, recursion, timeStamp);

					if (time > newestTime) {
						newestTime = time;
					}

					continue;
				}
				String fileName = new String(localFileName.getBytes(FTP_SRC_ENCODING), FTP_DEST_ENCODING) + "/"
						+ new String(file.getName().getBytes(FTP_SRC_ENCODING), FTP_DEST_ENCODING);
				File localFile = new File(fileName);

				if (!localFile.getParentFile().exists()) {
					localFile.getParentFile().mkdirs();
				}

				if (!file.getName().endsWith(validate)) {
					continue;
				}

				Long comperTime = file.getTimestamp().getTimeInMillis();
				if (comperTime <= timeStamp) {
					continue;
				}
				log.info("该文件的最新修改时间为：" + comperTime);
				this.downloadFile(ftpClient, file, localFile);

				// 若当前读取的文件最新修改时间comperTime大于备选时间newestTime，替换之
				if (comperTime > newestTime) {
					log.debug("出现过在这里" + comperTime);
					newestTime = comperTime;
				}
			}
		} finally {
			log.info(newestTime);
			IOUtils.closeQuietly(os);
		}
		return newestTime;
	}
	
	/**
	 * 本地上传文件到ftp
	 * @author:JSM
	 * @date 2018年5月16日  
	 * @param localPath
	 * @param uploadPath
	 */
	public boolean uploadFileFromLocal(String localPath, String uploadPath, boolean isPasv) throws Exception {
		if (StringUtils.isBlank(localPath)) {
			throw new CommonException(CommonErrorType.COMMON_DATA_NULL, ":上传的信息为空");
		}
		if(isPasv) {
			this.ftpClient.enterLocalPassiveMode();
		}
		File file = new File(localPath);
		FileInputStream input = FileUtils.openInputStream(file);
		return this.uploadFile(input, uploadPath, file.getName());
	}
	
	/**
	 * 本地上传文件到ftp
	 * @param localPath 本地的文件路径
	 * @param uploadPath 要上传的路径
	 * @return
	 * @throws Exception
	 */
	public boolean uploadFileFromLocal(String localPath, String uploadPath, String uploadName) throws Exception {
		if (StringUtils.isBlank(localPath)) {
			throw new CommonException(CommonErrorType.COMMON_DATA_NULL, ":上传的信息为空");
		}
		FileInputStream input = FileUtils.openInputStream(new File(localPath));
		return this.uploadFile(input, uploadPath, uploadName);
	}

	/**
	 * 通过byte上传文件
	 */
	public boolean uploadFile(byte[] byteFile, String uploadPath, String uploadName) throws Exception {
		if(byteFile == null) {
			throw new CommonException(CommonErrorType.COMMON_DATA_NULL, ":上传的信息为空");
		}
		InputStream input = new ByteArrayInputStream(byteFile);
		return this.uploadFile(input, uploadPath, uploadName);
	}

	/**
	 * 上传文件通过流的方式
	 * @param input
	 * @param uploadPath
	 * @param uploadName
	 * @return
	 * @throws Exception
	 */
	public boolean uploadFile(InputStream input, String uploadPath, String uploadName) throws Exception {
		// 设置编码，设置格式
		try {
			this.ftpClient.setControlEncoding(FTP_DEST_ENCODING);
			this.ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			if (input == null) {
				throw new CommonException(CommonErrorType.COMMON_DATA_NULL, ":上传的信息或文件名为空");
			}
			// 判断文件目录是否存在，不存在就创建
			if (!this.ftpClient.changeWorkingDirectory(uploadPath)) {
				log.info("新建目录为：" + uploadPath);
				this.createDirectory(uploadPath);
				log.info("目录创建结果为：" + this.ftpClient.changeWorkingDirectory(uploadPath));
			}

			return this.ftpClient.storeFile(uploadName, input);
		} catch (IOException e) {
			log.error("ftp文件上传失败", e);
			throw new CommonException(CommonErrorType.COMMON_ERROR, ":文件上传失败");
		} finally {
			IOUtils.closeQuietly(input);
		}
	}

	/**
	 * 删除文件的方法
	 * 
	 * @param fileName
	 * @param filePath
	 * @return
	 * @throws IOException
	 */
	public boolean removeFile(String fileName, String filePath) throws IOException {
		boolean isHadPath = this.ftpClient.changeWorkingDirectory(filePath);
		if (isHadPath) {
			return this.ftpClient.deleteFile(fileName);
		}
		return false;
	}

	/**
	 * ftp文件名修改
	 */
	public boolean reNameFile(String fromName, String toName) throws Exception {
		if (StringUtils.isBlank(fromName) && StringUtils.isBlank(toName)) {
			throw new CommonException(CommonErrorType.COMMON_DATA_NULL, "：参数有空");
		}
		this.ftpClient.setControlEncoding(FTP_DEST_ENCODING);
		try {
			this.ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			return this.ftpClient.rename(fromName, toName);
		} catch (IOException e) {
			log.error("ftp文件名修改失败", e);
			throw new CommonException(CommonErrorType.COMMON_ERROR, "：文件名修改失败");
		}
	}

	/**
	 * ftp讀取文件
	 */
	public List<String> readFileFormFTP(String ftpPath) throws Exception {
		log.info("开始读取绝对路径" + ftpPath + "文件!");
		boolean isHadPath = this.ftpClient.changeWorkingDirectory(ftpPath);
		log.info("检查路径结果：" + isHadPath);
		String[] listName = ftpClient.listNames();
		List<String> xmlList = new ArrayList<String>();
		for (String fileName : listName) {
			String resultXml = readFtpToString(ftpPath, fileName);
			xmlList.add(resultXml);
			try {
				ftpClient.deleteFile(ftpPath + fileName);
			} catch (Exception e) {
				log.error("删除文件出错，无法正确删除", e);
				throw new CommonException(CommonErrorType.COMMON_ERROR, "删除文件出错，无法正确删除");
			}
		}
		return xmlList;
	}

	/**
	 * 把文件内容读取成字符串
	 */
	public String readFtpToString(String ftpPath, String fileName) throws Exception {
		log.info("當前文件名為：" + ftpPath + "/" + fileName);
		InputStream in = null;
		String resultXml = null;
		this.ftpClient.changeWorkingDirectory(ftpPath);
		ftpClient.enterLocalPassiveMode();
		try {
			in = ftpClient.retrieveFileStream(fileName);
			if (in != null) {
				byte[] byteStr = IOUtils.toByteArray(in);
				resultXml = new String(byteStr);
				ftpClient.completePendingCommand();
			} else {
				log.error("文件为空，不进行读取");
			}

		} catch (Exception e) {
			log.error("讀取文件失敗", e);
			throw new CommonException(CommonErrorType.COMMON_ERROR, "删除文件出错，无法正确删除");
		} finally {
			IOUtils.closeQuietly(in);
		}
		return resultXml;
	}

	/**
	 * 判断指定文件是都存在修改
	 * 
	 * @param ftpPath
	 * @param fileName
	 * @param thisTimeStamp
	 * @return
	 * @throws Exception
	 */
	public Long judgeFileIsFix(String ftpPath, String fileName, Long thisTimeStamp) throws Exception {
		this.ftpClient.enterLocalPassiveMode();
		this.ftpClient.changeWorkingDirectory(ftpPath);
		FTPFile[] listFtp = ftpClient.listFiles();
		for (FTPFile ftp : listFtp) {
			if (ftp.getName().equals(fileName)) {
				Long fileTimeStamp = ftp.getTimestamp().getTimeInMillis();
				log.info("当前查看的文件的最新修改时间为：" + new Date(fileTimeStamp));
				if (fileTimeStamp > thisTimeStamp) {
					log.info("文件存在新改动。改动时间为:" + new Date(fileTimeStamp));
					return fileTimeStamp;
				}
			}
		}
		return thisTimeStamp;
	}

	/**
	 * 循环创建文件夹
	 * 
	 * @param directoryPath
	 * @throws Exception
	 */
	public void createDirectory(String directoryPath) throws Exception {
		String startPath = "/";
		String[] paths = StringUtils.split(directoryPath, "/");
		for (String str : paths) {
			if (StringUtils.isBlank(str)) {
				break;
			}
			startPath = startPath + str + "/";
			if (!this.ftpClient.changeWorkingDirectory(startPath)) {
				this.ftpClient.makeDirectory(startPath);
			}
		}
	}
	
//	public static void main(String[] args) throws Exception {
//		ConnectParam connectParam = new ConnectParam();
//		connectParam.setHost("192.168.137.2");
//		connectParam.setPassword("19953y2r");
//		connectParam.setPort(21);
//		connectParam.setUsername("JSM");
//		SimpleFTPOper simpleFTPOper = new SimpleFTPOper(connectParam);
//		simpleFTPOper.connect();
//		simpleFTPOper.uploadFileFromLocal("F:/迅雷下载/LB_GHOST_WIN10_X64_V2017_11.iso", "/temp/a/b/c/d/e/", "testInfo.iso");
//	}
}
